package com.yundingai.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.yundingai.domain.dto.RoomInfoDTO;
import com.yundingai.domain.vo.RoomSdpVo;
import com.yundingai.service.RoomService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author zhaoj
 * @Date 2025/7/24 13:39
 * @Description 处理和自习室相关的请求与自习室状态控制(暂时没有加入用户参加和退出的记录, 后续如需要记录付费, 则需要增项)
 **/
@Slf4j
@Service
public class RoomServiceImpl implements RoomService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * @Author zhaoj
     * @Date 2025/7/25 14:13
     * @Description 返回现存所有的房间列表，每次查询会刷新userList字段，删除过期的房间存储
     * @Return java.util.List<java.lang.Object>
     **/
    @Override
    public List<RoomInfoDTO> getRoomList() {
        Set<String> roomSet = stringRedisTemplate.opsForSet().members("roomList");
        if (Objects.isNull(roomSet)) {
            return null;
        }
        //构建房间列表，如果有过期关闭的房间，更新roomList
        List<RoomInfoDTO> roomList = new ArrayList<>();
        roomSet.forEach(roomId -> {
            if (!redisTemplate.hasKey("room:" + roomId)) {
                redisTemplate.opsForSet().remove("roomList", roomId);
            } else {
                Map<Object, Object> map = redisTemplate.opsForHash().entries("room:" + roomId);
                RoomInfoDTO room = JSONObject.parseObject(JSONObject.toJSONString(map), RoomInfoDTO.class);
                room.setPassword(null);
                roomList.add(room);
            }
        });
        return roomList;
    }

    /**
     * @Author zhaoj
     * @Date 2025/7/25 14:29
     * @Description 处理用户加入房间的逻辑：房间不存在返回错误，房间存在正常加入，如果房间为空，则取消房间的ttl设置
     * @Params [msg]
     * @Return void
     **/
    @Override
    public boolean handleJoin(RoomSdpVo msg) {
        //判断房间是否存在或过期
        if (!redisTemplate.hasKey("room:" + msg.getRoomId())) {
            redisTemplate.opsForSet().remove("roomList", msg.getRoomId());
            return false;
        }
        //如果房间为空则删除过期时间
        if (Boolean.FALSE.equals(redisTemplate.opsForSet().isMember("roomList", msg.getRoomId()))) {
            redisTemplate.persist("room:" + msg.getRoomId());
            redisTemplate.opsForHash().put("room:" + msg.getRoomId(), "isActive", true);
        }
        //判断房间是否满员
        Integer capacity = (Integer) redisTemplate.opsForHash().get("room:" + msg.getRoomId(), "capacity");
        Integer memberCount = (Integer) redisTemplate.opsForHash().get("room:" + msg.getRoomId(), "memberCount");
        if (capacity != null && memberCount != null && capacity > memberCount) {
            //执行加入房间逻辑
            //增加房间现有人数
            redisTemplate.opsForHash().increment("room:" + msg.getRoomId(), "memberCount", 1);
            initUserStatus(msg.getFromUserId(), msg.getRoomId());
            return true;
        }
        return false;
    }

    @Override
    public void handleExit(RoomSdpVo msg) {
        String roomId = msg.getRoomId();
        String userId = msg.getFromUserId();
        handleExit(roomId, userId);
    }

    @Override
    public void handleExit(String roomId, String userId) {
        log.info("检测到用户{}离开房间{}, 处理中...", userId, roomId);
        Integer count = (Integer) redisTemplate.opsForHash().get("room:" + roomId, "memberCount");
        if (count == null) count = 0;
        log.info("在线：{}人", count);
        if (count > 1) {
            redisTemplate.opsForHash().increment("room:" + roomId, "memberCount", -1);
        } else {
            redisTemplate.opsForHash().put("room:" + roomId, "isActive", false);
            redisTemplate.opsForHash().put("room:" + roomId, "memberCount", 0);
            log.info((String) redisTemplate.opsForHash().get("room:" + roomId, "memberCount"));
            redisTemplate.expire("room:" + roomId, 10, TimeUnit.MINUTES);
        }
    }

    /**
     * @Author zhaoj
     * @Date 2025/7/24 17:24
     * @Description 创建自习室房间，并将前端传回的房间信息存入redis: 房间列表：set，房间信息：map; 不维护用户列表
     * @Params RoomInfoDTO
     * @Return code 200/400
     **/
    @Override
    public void createRoom(RoomInfoDTO room) {
        String roomId = UUID.randomUUID().toString().replace("-", "").toUpperCase();
        room.setRoomId(roomId);
        room.setMemberCount(0);
        room.setIsActive(false);
        //默认设置10分钟无人删除房间信息
        room.setTtlMinute(10);
        room.setCreateTime(new Date());
        //todo 这里添加数据库日志记录操作
        //更新房间列表
        stringRedisTemplate.opsForSet().add("roomList", roomId);
        //存储房间信息
        Map<String, Object> roomMap = JSONObject.from(room);
        redisTemplate.opsForHash().putAll("room:" + roomId, roomMap);
        //设置房间过期时间
        redisTemplate.expire("room:" + roomId, room.getTtlMinute(), TimeUnit.MINUTES);
        log.info("创建了新的房间:{}", roomId);
    }

    @Override
    public void handleHeartBeat(RoomSdpVo message) {
        String roomId = message.getRoomId();
        String userId = message.getFromUserId();
        log.info("收到用户:{}的心跳信息", userId);
        initUserStatus(userId, roomId);
    }

    private void initUserStatus(String userId, String roomId) {
        log.info("刷新用户:{}的在线状态", userId);
        //维护用户离线记录
        stringRedisTemplate.opsForValue().set("online:" + userId, "true", 1, TimeUnit.MINUTES);
        //维护一个用户列表，用于遍历在线用户进行离线检测
        stringRedisTemplate.opsForHash().put("online", userId, roomId);
    }

    /**
     * @Author zhaoj
     * @Date 2025/7/28 17:07
     * @Description 定期清理过期用户状态与房间状态
     * @Params []
     * @Return void
     **/
    @Scheduled(fixedRate = 60000)
    public void handleOffline() {
        log.info("开始离线检测...");
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries("online");
        userMap.forEach((key, value) -> {
            log.info(Objects.requireNonNull(stringRedisTemplate.opsForValue().get("online:" + key)));
            if (value != null && !stringRedisTemplate.hasKey("online:"+key)) {
                log.info("检测到用户离线");
                stringRedisTemplate.delete("online:" + key);
                stringRedisTemplate.opsForHash().delete("online", key);
                handleExit((String) value, (String) key);
            }
        });
        log.info("离线检测完成");
        log.info("开始空闲房间检测");
        Set<String> roomList = stringRedisTemplate.opsForSet().members("roomList");
        if (roomList != null) {
            roomList.forEach(roomId -> {
                if (!redisTemplate.hasKey("room:" + roomId)) {
                    log.info("检测到房间过期{}", roomId);
                    stringRedisTemplate.opsForSet().remove("roomList", roomId);
                }
            });
        }
        log.info("空闲房间检测完成");
    }

    /**
     * @Author zhaoj
     * @Date 2025/7/28 14:51
     * @Description 获取在线用户数量
     * @Params []
     * @Return java.lang.Integer
     **/
    @Override
    public Integer getUserCount() {
        return Math.toIntExact(stringRedisTemplate.opsForHash().size("online"));
    }

    /**
     * @Author zhaoj
     * @Date 2025/7/28 17:03
     * @Description 获取当前自习室数量
     * @Params []
     * @Return java.lang.Integer
     **/
    @Override
    public Integer getRoomCount() {
        return Math.toIntExact(Objects.requireNonNull(stringRedisTemplate.opsForSet().size("roomList")));
    }

    /**
     * @Author zhaoj
     * @Date 2025/7/28 20:42
     * @Description 密码验证通过后，返回当前房间信息, 密码为空则直接返回房间信息
     * @Params [roomId, password]
     * @Return com.yundingai.domain.dto.RoomInfoDTO
     **/
    @Override
    public RoomInfoDTO varifyPwd(String roomId, String password) {
        if (!password.isEmpty()) {
            String pass = Objects.requireNonNull(redisTemplate.opsForHash().get("room:" + roomId, "password")).toString();
            if (!pass.equals(password)) {
                return null;
            }
        }
        Map<Object, Object> entries = redisTemplate.opsForHash().entries("room:" + roomId);
        return JSONObject.parseObject(JSONObject.toJSONString(entries), RoomInfoDTO.class);
    }
}
